package com.jin.webvideo.base.rpc;

import org.mybatis.logging.Logger;
import org.mybatis.logging.LoggerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;

public class ClassPathClientScanner extends ClassPathBeanDefinitionScanner {
    private static final Logger LOGGER = LoggerFactory.getLogger(ClassPathClientScanner.class);
    private boolean lazyInitialization = true;
    private Class<?> factoryBeanClass;
    private String httpClientBeanName;
    private String environmentBeanName;

    public void setFactoryBeanClass(Class<? extends FactoryBean> factoryBeanClass) {
        this.factoryBeanClass = factoryBeanClass;
    }

    public void setHttpClientBean(String clientFactoryBeanName) {
        this.httpClientBeanName = clientFactoryBeanName;
    }

    public void setEnvironmentBeanName(String environmentBeanName) {
        this.environmentBeanName = environmentBeanName;
    }

    public ClassPathClientScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }
    
    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (beanDefinitions.isEmpty()) {
            LOGGER.warn(() -> {
                return "No local client was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.";
            });
        } else {
            this.processBeanDefinitions(beanDefinitions);
        }
        return beanDefinitions;
    }


    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        Iterator<BeanDefinitionHolder> iterator = beanDefinitions.iterator();
        while(iterator.hasNext()) {
            BeanDefinitionHolder holder = iterator.next();
            if (holder.getBeanDefinition() instanceof GenericBeanDefinition definition) {
                definition.setLazyInit(this.lazyInitialization);
                String beanClassName = definition.getBeanClassName();

                definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
                definition.setBeanClass(this.factoryBeanClass);
                if (!StringUtils.hasText(this.environmentBeanName)) {
                    this.environmentBeanName = "environment";
                    LOGGER.warn(() -> {
                        return "Creating RpcFactoryBean with name '" + holder.getBeanName() + "use default environment";
                    });
                }
                LOGGER.debug(() -> {
                    return "Creating RpcFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' rpcInterface environment is '"+environmentBeanName+"' and httpClient is '" +httpClientBeanName+"'";
                });
                definition.getPropertyValues().add("environment", new RuntimeBeanReference(environmentBeanName));
                if (StringUtils.hasText(this.httpClientBeanName)) {
                    definition.getPropertyValues().add("httpClient", new RuntimeBeanReference(this.httpClientBeanName));
                }
                definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            }
        }
    }


    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }

    protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) {
        if (super.checkCandidate(beanName, beanDefinition)) {
            return true;
        } else {
            LOGGER.warn(() -> {
                return "Skipping MapperFactoryBean with name '" + beanName + "' and '" + beanDefinition.getBeanClassName() + "' mapperInterface. Bean already defined with the same name!";
            });
            return false;
        }
    }
}
